from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
from utils import set_models, print_dict


def print_log(best_param, best_score):
    print("已在训练集中使用KFord交叉验证完毕, 相应参数及得分如下:")
    print_dict(best_param)

    print("score = {:.4f}".format(best_score))


def print_adjust(adjust: dict):
    if len(adjust) == 0:
        return

    print("使用GridSearchCV搜索最佳参数, 其中需要搜索的参数共{:3d}个,分别为:".format(len(adjust)))
    for key in adjust:
        print("{:4s}\t".format(key), end="")
    print("")
    print("*"*50)


class FineTune:
    """
    @:param model:模型\n
    @:param parameters:模型参数\n
    @:param target:评价函数,值得注意的是只允许为单目标，若使用多目标请选择加权用法\n
    @:param cv:K-Ford，k折\n
    """
    def __init__(self, model, parameters, target, cv=5):
        self.model = model
        self.parameters = parameters
        self.score_fun = target

        self.cv = cv

    def _split_params(self):
        """
        将参数划分为固定参数和调整参数两部分
        :return: fixed_params:dict, adjust_params:dict
        """
        fixed_params = dict()
        adjust_params = dict()

        for key in self.parameters:
            if isinstance(self.parameters[key], list) or isinstance(self.parameters[key], tuple):
                adjust_params[key] = self.parameters[key]

            else:
                fixed_params[key] = self.parameters[key]
        # 若需要调整，则输出相应信息
        print_adjust(adjust_params)
        return fixed_params, adjust_params

    def get_ready(self, fixed_params):
        """
        使用固定参数初始化模型\n
        >>> clf = SVC(**fixed_params)\n
        >>> return clf
        :param fixed_params: 固定参数
        :return: 初始化的模型
        """
        return set_models(self.model, fixed_params)

    def tune(self, sets):

        fixed, adjust = self._split_params()

        clf = self.get_ready(fixed)
        if len(adjust) > 0:
            clf = GridSearchCV(clf, adjust, scoring=self.score_fun, n_jobs=-1, cv=self.cv, error_score=-1)

            clf.fit(sets[0], sets[1])

            clf = clf.best_estimator_
            best_params = clf.best_params_
            best_score = clf.best_score_
            print_log(best_params, best_score)
        return clf
